js-14 正则篇

一、正则RegExp

用特殊的字符组成验证字符串的规则,用来检索或者替换字符串,检测表单输入的信息是否正确,默认只匹配一次;

正则:也叫做规则,让计算机能够读懂人类的规则(正则都是操作字符串的);

用处:比如注册,邮箱的验证,表单提交等;

Reg 正则 Exp表达式

正则下面的方法:exectest

字符串下面的方法:searchmatchreplacesplit

常用:testsearchmatchreplace

二、创建规则

①构造函数创建:new RegExp(表达式,修饰符);一般用在需要用变量的时候

②字面量创建:/表达式/修饰符 例如: var a=/**/;

var re = /a/g   // a为字符串   推荐使用
var re = new RegExp(a)  // a为变量
var re = new RegExp("a")   // a为字符串

三、正常检测方法

检测str中是否有符合reg规则的字符串

  • test();reg.test(str);有返回true,没有返回false;
  • exec();reg.exec(str);有返回数组,没有返回null

exec可以循环匹配,放在while循环,每次循环从下一次开始查找,当找不到时会返回null,循环内部通过RegExp.$1获取小括号内容,$2为第二个小括号,以此类推;

var str = '?name=hny&age=18'
var o = {}
var res;
let reg = /(\w+)=(\w+)/g
while(res = reg.exec(str)){
  // o[res[1]] = res[2]
  o[RegExp.$1] = RegExp.$2
}
console.log(o)

注意:正则需要用变量保存, 不能直接在while使用,否则获取不到上一次,如:

while(/(\w+)=(\w+)/g.exec(str)){} // 报错;

四、修饰符

  • i:不区分大小写,默认区分大小写

  • g:匹配全局,默认只匹配一次

  • s:不匹配.点的规则,解决\n \r,点默认不匹配换行符,加上可以匹配换行符

  • u:根据unicode解析字符(影响\w)

如果既不区分大小写也不区分一次,则加ig

五、正则中的元字符

使用特殊字符表示特殊含义

\ 转义符,转义表示本身;

需要转义的符号:[ ] { } ( ) . ? * + ^ \ / | ;

本来这个字符它有自身的意思,但是加上反斜杠就成了另外一个意思;

比如:'anb',这里表示anb,'a\nb' 这里表示a换行b;

元字符说明
\s空格:(空格、制表符、换行符)
\S非空格
\d数字
\D非数字,同[^0-9]相同
\w字符(字母和数字及下划线)
\W非字符
.除了换行符以外的任意字符, 加s还可以匹配换行符
n*匹配零个或多个n
n+匹配至少一个n的字符串
n?匹配零个或一个n
^行首匹配
$行尾匹配
``
\n匹配换行符
\r匹配回车字符
\t匹配制表符
\f换页符
\b匹配一个单词边界,也就是单词和空格间的位置
\B匹配非单词边界,与上一个例子反过来
/[\u4e00-\u9fa5]/匹配汉字

六、正则中的小括号

  1. ()里面的内容表示捕获分组,()会把每个分组里的匹配结果保存起来,使用$n获取;
  2. /(a)(b)(c)\1\2\3/ \1代表a和小括号a是一样的内容、\2代表b,
  3. (xyz)+ 匹配至少一个(xyz),括号表示一体,
  4. /a(b|c)d/ 匹配abd,acd;
  5. hehe{3} 是e匹配3次,(hehe){3},是hehe单词匹配3次;

\1$1 匹配第一个分组中的内容

\2$2 匹配第二个分组中的内容

\3$3 匹配第三个分组中的内容

\n只能用在正则表达式中,$n只能用在正则表达式之外的地方;

'2018-02-11'.replace(/(\d{4})\-(\d{2})\-(\d{2})/g,'$1/$2/$3')  // '2018/02/11'
'18782832656'.replace(/(\d{3})(\d{4})(\d{4})/,'$1****$3')
let str = 'abbkejsbcccwqaa'
str.match(/(\w)\1*/g);  // ['a','bb','k'.....]

// 判断双花括号的内容
let reg = /\{\{(.*)\}\}/

reg.test(str) // 返回true或false,可以通过RegExp.$1来获取上一次匹配的结果;

RegExp.$1 第一个分组; 获取第一个分组有小括号的分组的匹配结果;

手机中间的*

var str = '13388889999';
var re = /(\d{3})(\d{4})(\d{4})/   利用函数;
str.replace(re,function($0,$1,$2,$3){
    return $1 + '****' + $3;
}

也可以:

var str = "18210399098";
var reg = /\d{4}/ig;
reg.lastIndex = 3;    //从第三位开始匹配;
console.log(str.replace(reg.exec(str), "****"));

具名组匹配

为每组匹配指定名字(?<Groupname>)

const time = '2017-09-11'
const reg = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/u
time.replace(reg,'$<day>/$<month>/$<year>')
let result = reg.exec(time)
console.log(result.groups) // 输出试试

如果需要具名组引用:使用\k<name>

/(?<word>[a-z])\k<word>/

格式化时间

let reg = /(\d{4})\D(\d{2})\D(\d{2}).*(\d{2}):(\d{2}):(\d{2})/gi
console.log(new Date().toLocaleString().replace(reg,'$1-$2-$3 $4:$5:$6'))

七、正则中的中括号

字符类:一组相似的字符,

[abc]整体代表一个字符,内部为或的关系。

[^abc]:排除abc;中括号中使用^,表示排除;

[a-z]: 范围a-z;可以是[e-j];代表一个字符

八、正则中的大括号(量词)

{0,} 简写n* 匹配零个或多个n

{1,} 简写n+ 匹配至少一个n的字符串;

{0,1}简写? 匹配零个或一个n

{n} 匹配n次

{n,m} 匹配n到m次

{n,} 至少匹配n次;

九、正则中字符串的方法

replace

字符串.replace(正则,新的字符);将正则匹配到的字符更换新字符;

  • 第二个参数也可以是一个函数:函数第一个参数是正则匹配成功的字符,通常是$0,
    • 函数的参数:
      • 第一个参数为匹配成功的字符$0;
      • 第二个参数:正则的第一个小括号$1;
      • 第三个参数:正则的第二个小括号$2;以此类推;
  • 第二个参数字符串的$符作用:
$1、$2、...、$99与 regexp 中的第 1 到第 99 个子表达式相匹配的文本。
$&与 regexp 相匹配的子串。当不加小括号就使用$&
$'位于匹配子串左侧的文本。
$`位于匹配子串右侧的文本。
$$直接量符号。

例子:分组操作,匹配子项;

var str = '2019-4-17';
var re = /(\d+)(-)/g; 
str.replace(re,function($0,$1,$2){     
    //$0匹配成功的字符,$1匹配第一个小括号(子项)的,$2匹配第二个小括号(子项);
    $2  //为第二个小括号的匹配项,为-,可以修改这个项;
}

str.replace(reg,function(cc){console.log(cc)});

str配合正则使用,正则加g,有多少个则替换多少个,如果正则加了g,使用这个方法和forEach是一样的;

// 例子1
var str = "北京,新疆,湖南,黑龙江,乌鲁木齐";
var reg = /北京|黑龙江|乌鲁木齐/g
var aa = str.replace(reg,function(val){ //循环替换里面的关键字  相当于执行了3遍
    console.log(val);
    var stm = "";
    for(var i=0;i<val.length;i++){ //循环文字的长度,将reg替换掉;
        stm+="*";
    }
    return stm;
});
console.log(aa);  // **,黑龙江,新东方,****,西藏

// 例子2 转换驼峰命名法;
const toCameLize = str.replace(/-(\w)/g,(_,c)=>(c ? c.toUpperCase() : ''))
console.log(ab-cd-ef) // abCdEf;

split

str.split(reg);以符合正则的内容分割

第二个参数可选的,新数组的长度;

var str = '2019-4-17 15:25:25';
var re = /-|\s|:/g;    // 以-或\s或:分隔

match

如果有就返回匹配到的字符组成的数组,没有就返回null;

默认只匹配一次,匹配多次可以加g

str.match(reg)

var str = "123hdsajkfd4355jkdslaf888"
str.match(/\d+/g);   //默认查找一次   g查找全部
输出之后:ary[123,4355,888]   //输入全部数字;

var arr = ['book','slice','root','dlick'];
var arr1 = arr.filter(value=>value.match(/oo/g));

返回第一次出现的下标;如果没有,则返回-1;像字符串的indexOf()

字符串.search(reg)

lastIndex

纪录下一次要查找的位置的下标

正则.lastIndex

查找完为null之后从0开始,用正则调用,需要配合修饰符g;如果前面为1,则下一次为2;

var str = "18210399098";
var reg = /\d{4}/ig;
reg.lastIndex = 3;    //从第三位开始匹配;
console.log(str.replace(reg.exec(str), "****"));

十、高级用法

前瞻(?=)、后顾(?<=)、负前瞻(?!)、负后顾(?<!)

// 前瞻:
e(?=f) e后边是f就匹配  f不会放进结果里;   ?:则会放进结果里;
// 负前瞻:
e(?!f) e后边不是f就匹配   ?!f  将=换成!  则为取非;
// 后顾:
(?<=f)e e前面是f就匹配
// 负后顾:
(?<!f)e e前面不是f就匹配

例子

let a = '123ab'
a.match(/\d+(?=ab)/) // 123   ab不会放进结果里
a.match(/\d+(?:ab)/)  // 123ab  ab会放进结果里
a.match(/\d+(?!bc)/)  //123   后面不能是bc,bc是不会放入到匹配结果内的;
a.match(/(?<=\d)[a-z]+/)   //ab   匹配紧跟着数字后的字母;

组合使用

   //匹配字母之间的数字;
let a = 'qee132ab'; 
a.match(/(?<=[a-z]+)\d+(?=[a-z]+)/)  //  ?=匹配为ab的;
//["132", index: 3, input: "qee132ab", groups: undefined]

组合符:?:

理解?:需要理解捕获分组和非捕获分组的概念

()表示捕获分组,()会把每个分组里的匹配的值保存起来,使用$n(n是一个数字,表示第n个捕获组的内容)

(?:)表示非捕获分组,和捕获分组唯一的区别在于,非捕获分组匹配的值不会保存起来

例子:格式化金钱

// 匹配的是后面3*n个数字的非单词边界(\B)
console.log('12345678'.replace(/\B(?=(?:\d{3})+(?!\d))/g, ','))   

/* 原理解析: 
 * 查找3n正整数和1个非数字, 8后面没有数字了, 因此需要加一个非数字
 * 其实不加?:也行,但大多数加上
 */

组合符:.*

(.*) 匹配直到后面条件的最后一个;

(.*?) 匹配到后面条件下一个符合条件;

贪婪模式与非贪婪模式

贪婪模式 :正则表达式在匹配的时候默认会尽可能多的匹配

非贪婪模式 :通过在限定符后加?叫非贪婪匹配;

比如:\d{3,6}默认匹配6个数字而不是3个,\d{3,6}?,匹配三次;

'abbbb'.replace(/ab+/,'-')   //则结果是'-';
'abbbb'.replace(/ab+?/,'-')   // 则结果是'-bbb';

// 大家好,我叫{{name}},今年{{age}}岁
/{{(.*)}}/   // 我叫{{直到}}岁;   贪婪模式匹配到结尾;
/{{(.*?)}}/   // 我叫{{}},今年{{}}岁;  添加?开启非贪婪模式;

十一、常用正则

var reg = {
        phone: /^1\d{10}$/
        qq : /^[1-9][0-9]{4,9}$/
        email : /^\w+@\w+\.(com|cn|com.cn|wang|cc|xin|ren|info|xyz|site|club|win|net|org)$/
        space : /^\s*|\s*$/g
        身份证:/^[1-9]\d{14}|[1-9]\d{17}|[1-9]\d{16}x$/
        匹配中文:/[\u4e00-\u9fa5]/g
        固话:/^0\d{2,3}-[1-9]\d{6,7}$/
        网址:/^[a-zA-Z]+:\/\/[^\s]+$/
        密码:/^(?=.*[a-zA-Z])(?=.*[1-9])(?=.*[\W]).{6,}$/;
        格式化金钱:/\B(?=(\d{3})+(?!\d))/g,
}

密码限制

  • (?=) 零宽断言,表示有;

  • (?=.*[a-zA-Z]) 后面必须有一位大写或小写字母

  • (?=.*\d) 后面必须有一位数字

  • (?=.*\W) 后面必须有一个非字母数字及下划线的特殊符号

后面必须跟点再写限制量词;

.* 表示匹配除了换行符以外的任意0个或多个;

必须包含字母 数字 及特殊符号,且6位以上

/^(?=.*[a-zA-Z])(?=.*[1-9])(?=.*[\W]).{6,}$/ 不能全部是数字或字母或符号

后面的一个点表示匹配一位换行符以外的字符,带了大括号匹配6位即可;

大小写字母 数字 特殊符号 必须包含两种

/^(?![0-9]+$)(?![a-z]+$)(?![A-Z]+$)(?!([^(0-9a-zA-Z)])+$).{6,}$/

至少有一个并且数字开头

/\d+[a-z]+/

匹配到汉字以及前面的字符

/[a-z0-9]*[\u4e00-\u9fa5+]/g.exec(this.innerHTML)

十二、正则示例

查找包含特定单词的句子

const str = "The apple tree originated in Central Asia. It is cultivated worldwide. Apple matures in late summer or autumn."

// 查找包含单词“ apple”的句子
str.match(/[^.!?]*\bapple\b[^.!?]*.?/gi)

// 输出结果
// => ["The apple tree originated in Central Asia.", "Apple matures in late summer or autumn."]

从文件中去除无效字符

const str = "https://en.wikipedia.org/"

str.replace(/[<>|:"*?\\/]+/g, '')
// => "httpsen.wikipedia.org"

用单个空格替换多个空格

const str = "  My    opinions may  have changed,    but not the fact that I'm right."

str.replace(/\s\s+/g, ' ')
// => " My opinions may have changed, but not the fact that I'm right."

此代码用空格(U + 0020)字符替换任何类型的空格字符,包括ASCII空格,制表符,换行符,回车符,垂直制表符和换页符。因此,如果回车符紧跟在制表符之后,它们将被空格替换。如果这不是我们的意图,并且只想替换相同类型的空格,请改用以下代码:

str.replace(/(\s)\1+/g, '$1').trim(); // 需要.trim 去除结尾跟开头的空格

限制用户只能输入数字或字母

const input1 = "John543";
const input2 = ":-)";
/^[A-Z0-9]+$/i.test(input1);    // → true
/^[A-Z0-9]+$/i.test(input2);    // → false

将网址变成链接

const str = "Visit https://en.wikipedia.org/ for more info.";
str.replace(/\b(https?|ftp|file):\/\/\S+[\/\w]/g, '<a href="$&">$&</a>')

// => "Visit <a href="https://en.wikipedia.org/">https://en.wikipedia.org/</a> for more info."

删除重复的单词

const str = "This this sentence has has double words."

str.replace(/\b(\w+)\s+\1\b/gi, '$1')

// => "This sentence has double words."

清除开始结束标签的空格

var trim = /^\s*|\s*$/g

总结大纲

image